package com.linzm.aspect;

import com.linzm.annotation.*;
import com.linzm.entry.CheckResult;
import javafx.util.Pair;

import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Date: 2023/11/9 19:14
 * @User linziming
 */
public class AspectHandler implements IAspectHandler {
    // Aspect切面类
    private static final Map<String/*匹配拦截路径*/, Pair<Object, CheckResult/*运行哪些通知*/>> aspectPointMap
            = new HashMap<>();

    public AspectHandler(Map<String, Object> iocMap) {
        init(iocMap);
    }

    @Override
    public Pair<Object, CheckResult> getAspectInstance(String pointCanonicalName) {
        for (Map.Entry<String, Pair<Object, CheckResult>> entry : aspectPointMap.entrySet()) {
            if (match(entry.getKey(), pointCanonicalName)) {
                return entry.getValue();
            }
        }
        return null;
    }

    private boolean match(String regex, String pointCanonicalName) {
        Pattern p = Pattern.compile(regex);
        Matcher matcher = p.matcher(pointCanonicalName);
        while (matcher.find()) {
            return true;
        }
        return false;
    }

    private static void init(Map<String, Object> iocMap) {
        // 获取 Aspcet 类
        List<Object> aspects = getAspect(iocMap);
        // 解析Aspcet
        parseAspect(aspects);
    }

    /**
     * 获取 Aspcet 类
     *
     * @param iocMap iocMap
     * @return List<Object>
     */
    private static List<Object> getAspect(Map<String, Object> iocMap) {
        List<Object> list = new ArrayList<>();
        if (iocMap.isEmpty()) {
            return list;
        }
        Iterator<Map.Entry<String, Object>> iterator = iocMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            if (entry.getValue().getClass().isAnnotationPresent(Aspect.class)) {
                list.add(entry.getValue());
                iterator.remove();
            }
        }
        return list;
    }

    /**
     * 解析Aspcet
     *
     * @param aspectBeans aspectBeans
     */
    private static void parseAspect(List<Object> aspectBeans) {
        aspectBeans.forEach(aspectBean -> {
            CheckResult checkResult = new CheckResult();
            String pointCutValue = "";
            Class<?> clazz = aspectBean.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(Pointcut.class)) {
                    Pointcut myPointcut = method.getAnnotation(Pointcut.class);
                    if (myPointcut == null) {
                        throw new RuntimeException("未定义pointcut");
                    }
                    pointCutValue = myPointcut.value();
                    continue;
                }
                if (method.isAnnotationPresent(Before.class)) {
                    Before myBefore = method.getAnnotation(Before.class);
                    if (myBefore == null) {
                        continue;
                    }
                    checkResult.setRunBefore(isRun(myBefore.value()));
                }
                if (method.isAnnotationPresent(Around.class)) {
                    Around myAround = method.getAnnotation(Around.class);
                    if (myAround == null) {
                        continue;
                    }
                    checkResult.setRunAround(isRun(myAround.value()));
                }
                if (method.isAnnotationPresent(After.class)) {
                    After myAfter = method.getAnnotation(After.class);
                    if (myAfter == null) {
                        continue;
                    }
                    checkResult.setRunAfter(isRun(myAfter.value()));
                }
                if (method.isAnnotationPresent(AfterReturning.class)) {
                    AfterReturning myAfterReturning = method.getAnnotation(AfterReturning.class);
                    if (myAfterReturning == null) {
                        continue;
                    }
                    checkResult.setRunAfterReturning(isRun(myAfterReturning.value()));
                }
                if (method.isAnnotationPresent(AfterThrowing.class)) {
                    AfterThrowing myAfterThrowing = method.getAnnotation(AfterThrowing.class);
                    if (myAfterThrowing == null) {
                        continue;
                    }
                    checkResult.setRunAfterThrowing(isRun(myAfterThrowing.value()));
                }
            }
            aspectPointMap.put(pointCutValue, new Pair<>(aspectBean, checkResult));
        });
    }

    /**
     * 是否运行该通知
     *
     * @param value value
     * @return boolean
     */
    private static boolean isRun(String value) {
        if (value.isEmpty()) {
            return false;
        }
        value = value.trim();
        if (!value.contains("()")) {
            throw new RuntimeException("切面注解有误");
        }
        return value.contains("myPointcut");
    }

}
